/***************************************************************
 *
 * (c) 2008 Markus Dittrich 
 *
 * This program is free software; you can redistribute it 
 * and/or modify it under the terms of the GNU General Public 
 * License Version 3 as published by the Free Software Foundation. 
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License Version 3 for more details.
 *
 * You should have received a copy of the GNU General Public 
 * License along with this program; if not, write to the Free 
 * Software Foundation, Inc., 59 Temple Place - Suite 330, 
 * Boston, MA 02111-1307, USA.
 *
 ****************************************************************/

/** STL includes */
#include <float.h>
#include <algorithm>

/** local includes */
#include "data_compute_engine.h"
#include "utilities.h"
#include "basic_definitions.h"

/** pull some names into namespace */
using std::vector;


/*****************************************************************
 *
 *
 * definition of HistogramDataEngine
 *
 *
 *****************************************************************/


/*****************************************************************
 *
 * public data members
 *
 *****************************************************************/

//----------------------------------------------------------------
// constructor
//----------------------------------------------------------------
HistogramComputeEngine::HistogramComputeEngine(int numBins, 
    bool normalize)
  :
    NumBins_(numBins),
    normalize_(normalize)
{
  // nothing do do
}


//----------------------------------------------------------------
// initialization function for data
//----------------------------------------------------------------
DataObjectPtr HistogramComputeEngine::compute(
    const vector<double>& data) 
{
  return compute_(data);
}


/*****************************************************************
 *
 * private data members
 *
 *****************************************************************/

//----------------------------------------------------------------
// function computing the actual histogram;
// NOTE: we ignore the passed x-data an bin only the data
// delivered as y-data
//----------------------------------------------------------------
DataObjectPtr HistogramComputeEngine::compute_(
    const vector<double>& data) 
{
  /* set up the bins */  
  double max = 0.0; 
  double min = 0.0;
  data_max(data.begin(), data.end(), max);
  data_min(data.begin(), data.end(), min);

  double binWidth = fabs(max-min)/NumBins_;
  vector<double> binLocations;
 
  /* NOTE: we need <= NumBins_ in the loop since we
   * one more separator than bins */
  for (int counter = 0; counter <= NumBins_; ++counter)
  {
    binLocations.push_back(min+counter*binWidth);
  }

  /* insert data into bins; 
   * NOTE: due to rounding errors followed by the cast, some
   * values can end up in the bin one past NumBins_; therefore
   * we create a sentinel bin at NumBins_+1 and fold all entries
   * back to NumBins_ once we are done */
  std::vector<double> binValues(NumBins_+1,0.0);
  FOREACH(double item, data)
  {
    int index = static_cast<int>(floor((item-min)/binWidth));
    binValues[index] += 1.0;
  }
  binValues[NumBins_-1] += binValues[NumBins_];
  binValues.pop_back();

  /* normalize if requested */
  if ( normalize_ )
  {
    int num_data = data.size();
    std::for_each(binValues.begin(), binValues.end(),
        std::bind2nd(divide_by<double,int>(),num_data));
  }

  return generate_data_object_(binLocations, binValues);
}


//----------------------------------------------------------------
// generate the actual data object based on bin values and 
// locations
//----------------------------------------------------------------
DataObjectPtr HistogramComputeEngine::generate_data_object_(
    const std::vector<double>& binLocations, 
    const std::vector<double>& binValues)
{
  /* create new DataObject */
  DataObjectPtr dataObject = DataObjectPtr(new DataObject(2));

  /* prepare histogram data, each histogram bar or rectangle
   * consists of four points */
  vector<double> dataItem;
  for (int counter = 0; counter < NumBins_; ++counter)
  {
    dataItem.push_back(binLocations[counter]);
    dataItem.push_back(0.0);
    dataObject->append(dataItem);
    dataItem.clear();

    dataItem.push_back(binLocations[counter]);
    dataItem.push_back(binValues[counter]);
    dataObject->append(dataItem);
    dataItem.clear();

    dataItem.push_back(binLocations[counter+1]);
    dataItem.push_back(binValues[counter]);
    dataObject->append(dataItem);
    dataItem.clear();

    dataItem.push_back(binLocations[counter+1]);
    dataItem.push_back(0.0);
    dataObject->append(dataItem);
    dataItem.clear();

    dataItem.push_back(binLocations[counter]);
    dataItem.push_back(0.0);
    dataObject->append(dataItem);
    dataItem.clear();
  }

  return dataObject;
}
